#include <map>

#include "literal-to-gprel.hpp"
//#include "machmode.h"
#include "nohp.hpp"
#include <tree-core.h>
#include <tree-ssa-operands.h>
#include <pass_manager.h>
#include <tree-pass.h>
#include <rtl.h>
#include <c-tree.h>
#include <stringpool.h>
#include <cgraph.h>
#include <expr.h>
#include <memmodel.h>
#include <emit-rtl.h>
#include <context.h>
#include <tree.h>
#include <toplev.h>
#include <print-tree.h>
#include <string.h>
#include <cfgloop.h>
#include <gimple-pretty-print.h>
#include <tree-pass.h>
//#include <coretypes.h>
#include <gimple.h>
#include <gimple-iterator.h>
namespace {
  const pass_data pass_data_sw_64_literal_to_gprel =
  {
    RTL_PASS,
    "literal_to_gprel",
    OPTGROUP_NONE,
    TV_NONE,
    0,
    0,
    0,
    0,
    TODO_df_finish
  };
};
class pass_sw_64_literal_to_gprel : public rtl_opt_pass {
public:
  pass_sw_64_literal_to_gprel(gcc::context *ctx) : rtl_opt_pass(pass_data_sw_64_literal_to_gprel, ctx){}
  virtual bool gate (function *){
    return true;
  }

  unsigned int execute(function *f){
    //debug_iv(f);
    rtx_insn *insn;
    std::map<int, rtx> hint_map;
    for (insn = get_insns (); insn; insn = NEXT_INSN (insn)){
      if (GET_CODE(insn) == CALL_INSN){
        auto pat = PATTERN(insn);
        if (GET_CODE(pat) != PARALLEL) continue;
        //注意parallel的第一条指令可能是set $0 call ...或者直接call ...
        auto pat0 = copy_rtx(XVECEXP(pat, 0, 0));
        auto pat_call = pat0;
        if (GET_CODE(pat_call) == SET){
          pat_call = XEXP(pat_call, 1);
        }
        auto callee = XEXP(pat_call, 0);
        if (GET_CODE(callee) != MEM){
          warning(0, "encounter an unrecognizable SIBCALL");
        }
        auto symbol = XEXP(callee, 0);
        if (GET_CODE(symbol) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P(symbol)){
          //debug(symbol);

          auto tmp = XSTR(symbol, 0);
          
          auto sym_slavename = XSTR(symbol, 0);
          if (strncmp(sym_slavename, "slave_", 6))
            sym_slavename = ggc_strdup(ACONCAT(("slave_", XSTR(symbol, 0), nullptr)));
          auto new_ref = gen_rtx_SYMBOL_REF(Pmode, IDENTIFIER_POINTER(get_identifier(sym_slavename)));
          SYMBOL_REF_FLAGS(new_ref) |= SYMBOL_FLAG_LOCAL | SYMBOL_REF_FLAGS(symbol);

          auto reg = gen_rtx_REG(Pmode, 27);
          XEXP(pat_call, 0) = gen_rtx_MEM(DImode, reg);
          start_sequence();
          emit_move_insn(reg, gen_rtx_PLUS(Pmode, gen_rtx_REG(Pmode, 29), gen_rtx_HIGH(Pmode, new_ref)));
          emit_move_insn(reg, gen_rtx_LO_SUM(Pmode, reg, new_ref));
          auto seq = get_insns();
          auto vec = gen_rtvec(5,
                               pat0,
                               gen_rtx_USE(Pmode, gen_rtx_REG(Pmode, 29)),
                               gen_rtx_USE(Pmode, new_ref),
                               gen_rtx_USE(Pmode, gen_rtx_CONST_INT(VOIDmode, 0)),
                               gen_rtx_CLOBBER(DImode, gen_rtx_REG(Pmode, 26))
                               );
          auto new_insn = emit_call_insn(gen_rtx_PARALLEL(Pmode, vec));
          end_sequence();
          //debug(insn);
          if (REG_NOTES(insn))
            REG_NOTES(new_insn) = copy_rtx(REG_NOTES(insn));
          if (CALL_INSN_FUNCTION_USAGE(insn))
            CALL_INSN_FUNCTION_USAGE(new_insn) = copy_rtx(CALL_INSN_FUNCTION_USAGE(insn));
          INSN_LOCATION(new_insn) = INSN_LOCATION(insn);
          //debug(new_insn);

          emit_insn_before(seq, insn);
          set_insn_deleted(insn);
        }
      }
    }
    return 0;
  }
};

void sw_64_literal_to_gprel::add_passes() {
  if (TARGET_SW_SLAVE){
    nohp::register_pass(new pass_sw_64_literal_to_gprel(g), "postreload", 1, PASS_POS_INSERT_AFTER);
  }
}
